# @Version        : 1.0
# @Update Time    : 2025/8/25 23:13
# @File           : chart_builders.py
# @IDE            : PyCharm
# @Desc           : 文件描述信息
from typing import Sequence, List, Dict, Tuple, Optional
from pyecharts import options as opts
from pyecharts.charts import Bar, Pie, Boxplot, Bar3D, Map
from pyecharts.commons.utils import JsCode
from pyecharts.globals import ThemeType


def init_opts(theme: ThemeType = ThemeType.LIGHT, width: str = "100%", height: str = "360px") -> opts.InitOpts:
    return opts.InitOpts(theme=theme, width=width, height=height)


def to_percent_pairs(a: Sequence[float], b: Sequence[float]) -> Tuple[List[Dict], List[Dict]]:
    res_a, res_b = [], []
    for x, y in zip(a, b):
        s = (x or 0) + (y or 0)
        pa = (x / s) if s else 0
        pb = (y / s) if s else 0
        res_a.append({"value": x, "percent": pa})
        res_b.append({"value": y, "percent": pb})
    return res_a, res_b


def build_stacked_percent_bar(
    x_axis: Sequence,
    a: Sequence[float],
    b: Sequence[float],
    title: str = "stack_bar",
    theme: ThemeType = ThemeType.LIGHT,
) -> Bar:
    sa, sb = to_percent_pairs(a, b)
    return (
        Bar(init_opts=init_opts(theme))
        .add_xaxis(list(x_axis))
        .add_yaxis("product1", sa, stack="stack1", category_gap="50%")
        .add_yaxis("product2", sb, stack="stack1", category_gap="50%")
        .set_series_opts(
            label_opts=opts.LabelOpts(
                position="right", formatter=JsCode("function(x){return Number(x.data.percent * 100).toFixed(0) + '%';}")
            )
        )
        .set_global_opts(title_opts=opts.TitleOpts(title=title))
    )


def build_rose_pie(
    data: Optional[Sequence[Tuple[str, float]]] = None,
    title: str = "玫瑰图",
    theme: ThemeType = ThemeType.LIGHT,
) -> Pie:
    from pyecharts.faker import Faker

    pairs = data or [list(z) for z in zip(Faker.choose(), Faker.values())]
    return (
        Pie(init_opts=init_opts(theme))
        .add(
            "",
            pairs,
            radius=["30%", "75%"],
            center=["45%", "50%"],
            rosetype="radius",
            label_opts=opts.LabelOpts(is_show=False),
        )
        .set_global_opts(title_opts=opts.TitleOpts(title=title))
    )


def build_boxplot(
    labels: Sequence[str],
    data_groups: Sequence[Sequence[Sequence[float]]],  # 每个系列一组二维数组
    series_names: Sequence[str],
    title: str = "BoxPlot-基本示例",
    theme: ThemeType = ThemeType.LIGHT,
) -> Boxplot:
    c = Boxplot(init_opts=init_opts(theme))
    c.add_xaxis(list(labels))
    for name, raw in zip(series_names, data_groups):
        c.add_yaxis(name, c.prepare_data(raw))
    c.set_global_opts(title_opts=opts.TitleOpts(title=title))
    return c


def build_bar3d_stack(
    x_data: Sequence[int],
    y_data: Sequence[int],
    stacks: int = 10,
    title: str = "Bar3D-堆叠柱状图示例",
    theme: ThemeType = ThemeType.LIGHT,
) -> Bar3D:
    import random

    def gen():
        data = []
        for j in x_data:
            for k in y_data:
                v = random.randint(0, 9)
                data.append([j, k, v * 2 + 4])
        return data

    bar3d = Bar3D(init_opts=init_opts(theme, height="480px"))
    for _ in range(stacks):
        bar3d.add(
            "",
            gen(),
            shading="lambert",
            xaxis3d_opts=opts.Axis3DOpts(data=list(x_data), type_="value"),
            yaxis3d_opts=opts.Axis3DOpts(data=list(y_data), type_="value"),
            zaxis3d_opts=opts.Axis3DOpts(type_="value"),
        )
    bar3d.set_global_opts(title_opts=opts.TitleOpts(title=title))
    bar3d.set_series_opts(stack="stack")
    return bar3d


def build_map_china(
    pairs: Optional[Sequence[Tuple[str, int]]] = None,
    title: str = "登录省份分布",
    theme: ThemeType = ThemeType.LIGHT,
) -> Map:
    from pyecharts.faker import Faker

    # 使用实际数据或模拟数据
    data = pairs or [list(z) for z in zip(Faker.provinces, Faker.values())]

    # 省份名称标准化（确保与PyEcharts地图匹配）
    province_mapping = {
        "北京市": "北京",
        "天津市": "天津",
        "上海市": "上海",
        "重庆市": "重庆",
        "广西壮族自治区": "广西",
        "内蒙古自治区": "内蒙古",
        "西藏自治区": "西藏",
        "新疆维吾尔自治区": "新疆",
        "宁夏回族自治区": "宁夏",
        "香港特别行政区": "香港",
        "澳门特别行政区": "澳门",
    }

    # 应用省份名称映射
    processed_data = []
    for province, count in data:
        normalized_province = province_mapping.get(province, province)
        processed_data.append((normalized_province, count))

    # 创建地图
    map_chart = (
        Map(init_opts=opts.InitOpts(theme=theme, width="100%", height="600px"))
        .add(
            series_name="登录次数",
            data_pair=processed_data,
            maptype="china",
            is_map_symbol_show=False,
        )
        .set_global_opts(
            title_opts=opts.TitleOpts(
                title=title, pos_left="center", title_textstyle_opts=opts.TextStyleOpts(font_size=16)
            ),
            visualmap_opts=opts.VisualMapOpts(
                max_=max(count for _, count in processed_data) if processed_data else 100,
                min_=min(count for _, count in processed_data) if processed_data else 0,
                is_piecewise=True,
                pos_left="left",
                pos_bottom="bottom",
                range_text=["高", "低"],
            ),
            legend_opts=opts.LegendOpts(is_show=False),
        )
        .set_series_opts(label_opts=opts.LabelOpts(is_show=False))
    )

    return map_chart
